package pl.ydp.components.mathcell.components.mathtext
{
	import flash.display.DisplayObject;
	import flash.text.TextLineMetrics;
	
	import mx.containers.Box;
	import mx.containers.HBox;
	import mx.containers.VBox;
	import mx.controls.Label;
	import mx.controls.Spacer;
	import mx.events.ResizeEvent;
	import mx.styles.CSSStyleDeclaration;
	import mx.styles.StyleManager;
	
	import pl.ydp.components.mathcell.mathcell.expressions.MathExpressionProcessor;
	
	//[Style(name="fontFamily",type="String",inherit="no")]
	//[Style(name="fontSize",type="uint",format="Number",inherit="no")]
	//[Style(name="color",type="uint",format="Color",inherit="no")]
	
	public class MathText extends HBox
	{
		public function MathText()
		{
			body = new HBox;
			body.setStyle("paddingLeft", 2);
			body.setStyle("paddingRight", 2);
			body.setStyle("horizontalGap", 0);
			body.setStyle("horizontalAlign", "left");
			body.setStyle("verticalAlign", "middle");
			body.addEventListener(ResizeEvent.RESIZE, resizeHandler);
			addChild(body);
			
			slashes = new Array;
		}
		
		private static var classConstructed:Boolean = classConstruct();
		
        private static function classConstruct():Boolean {
        	
            if (!StyleManager.getStyleDeclaration("MathText"))
            {
                var style:CSSStyleDeclaration = new CSSStyleDeclaration();
                style.defaultFactory = function():void
                {
					this.horizontalGap = 0;
                }
                StyleManager.setStyleDeclaration("MathText", style, true);

            }
            return true;
        }		
        
		
		//private static const OPERATOR_DIVIDE:String = "/";
		//private static const DIGITS:Array = new Array("0","1","2","3","4","5","6","7","8","9");
		//private static const OPERATOR_FRACTION:String = "|";
		
		// --------------------------------------------------------
		
		private var body:HBox;
		private var slashes:Array;
		
		// --------------------------------------------------------
		
		private var _text:String = "";
		
		public function set text(value:String):void{
			if (value != text){
				_text = value;
				regenerateText();
			}
		}
		
		public function get text():String{
			return _text;
		} 
		
		private var _autoSize:Boolean = false;
		
		public function set autoSize(value:Boolean):void{
			if (autoSize != value){
				_autoSize = value;	
				if (_autoSize){
					body.percentWidth = 100;
					body.setStyle("horizontalAlign", "center");
				} else {
					body.percentWidth = NaN;
					body.setStyle("horizontalAlign", "left");
				}
			} 
		}
		
		public function get autoSize():Boolean{
			return _autoSize;			
		}
				
		private var _alignDecimal:Boolean = false;
		
		public function set alignDecimal(ad:Boolean):void{
			_alignDecimal = ad;
		}
		
		public function get alignDecimal():Boolean{
			return _alignDecimal;
		}
		
		// --------------------------------------------------------
		
		private function clear():void{
			body.removeAllChildren();
			slashes = new Array;
		}
		
		private function regenerateText():void{
			
			clear();
			
			var fractionNormalized:String = text;
			
			var fractionFound:Boolean = false;
			var firstDivideOperatorFound:Boolean = false;
			var breakFractionAnalysis:Boolean;
			var fractionEndIndex:Number;
			var i:Number;
			
			for (i = 0 ; i < fractionNormalized.length ; i ++){
				if (fractionNormalized.charAt(i) == MathExpressionProcessor.OPERATOR_FRACTION){
					if (!fractionFound){
						
						breakFractionAnalysis = false;
						firstDivideOperatorFound = false;
						
						fractionEndIndex = i;
						
						for (var fi:Number = i+1 ; fi < fractionNormalized.length ; fi ++){
							breakFractionAnalysis = true;
					
							if (MathExpressionProcessor.DIGITS.indexOf(fractionNormalized.charAt(fi)) == -1){
								if (!firstDivideOperatorFound  &&   
									(fractionNormalized.charAt(fi) == MathExpressionProcessor.OPERATOR_DIVIDE_SYNONYMS[0]  ||
									fractionNormalized.charAt(fi) == MathExpressionProcessor.OPERATOR_DIVIDE_SYNONYMS[1]  ||
									fractionNormalized.charAt(fi) == MathExpressionProcessor.OPERATOR_DIVIDE_SYNONYMS[2])
									){
									firstDivideOperatorFound = true;
									breakFractionAnalysis = false;
								} else {
									fractionEndIndex = fi;
								}
							} else {
								breakFractionAnalysis = false;
							}
							
							if (breakFractionAnalysis){
								break;
							}
						}
						
						if (!breakFractionAnalysis){
							fractionEndIndex = fractionNormalized.length;
						}
						
						if (fractionEndIndex > i + 1){
							fractionFound = true;
						}
					}
				}
				
				if (!fractionFound){
					if (fractionNormalized.charAt(i) != MathExpressionProcessor.OPERATOR_FRACTION){
						addCharacter(fractionNormalized.charAt(i), body);
					}
				} else {
					addFraction(MathExpressionProcessor.replaceDivideOperators(fractionNormalized.substring(i+1, fractionEndIndex)), body);
					if (fractionNormalized.charAt(fractionEndIndex) == MathExpressionProcessor.OPERATOR_FRACTION)
						i = fractionEndIndex;
					else
						i = fractionEndIndex-1;
					fractionFound = false;
				}
			}
			updateDecimalDisplay();			
		}
		
		private function addCharacter(value:String, container:Box):void{
			var char:MathTextCharacter = new MathTextCharacter;
			char.text = value;
			//char.setStyle("backgroundColor", 0xff0000 + Math.floor(Math.random()*0xffff));
			container.addChild(char);
		}
		
		private function addFraction(value:String, container:Box):void{
			
			// numerator & denominator definition
			
			var numerator:String;
			var denominator:String;
			
			var divideOperatorIndex:Number = value.indexOf(MathExpressionProcessor.OPERATOR_DIVIDE);
			
			if (divideOperatorIndex == -1){
				numerator = value;
				denominator = "";
			}else {
				numerator = value.substring(0, divideOperatorIndex);
				denominator = value.substring(divideOperatorIndex+1);
			}
			
			// boxes
			
			var fractionBox:VBox = new VBox;
			fractionBox.setStyle("verticalGap", -3);
			
			var numeratorBox:HBox = new HBox;
			numeratorBox.setStyle("horizontalGap", 0);
			var denominatorBox:HBox = new HBox;
			denominatorBox.setStyle("horizontalGap", 0);
			
			var fractionSlashBox:Box = new Box;
			fractionSlashBox.percentWidth = 100;
			
			var fontSize:Number = getStyle("fontSize");
			if (isNaN(fontSize))
				fractionSlashBox.height = 1;
			else
				fractionSlashBox.height = Math.ceil(fontSize/16);
				
			var fontColor:uint = getStyle("color");
			if (divideOperatorIndex != -1){
				fractionSlashBox.setStyle("backgroundAlpha", 1);
				if (isNaN(fontColor))
					fractionSlashBox.setStyle("backgroundColor", "#000000");
				else 
					fractionSlashBox.setStyle("backgroundColor", fontColor);
			} else {
				fractionSlashBox.setStyle("backgroundAlpha", 0);
			}
			slashes.push(fractionSlashBox);
			
			fractionBox.addChild(numeratorBox);
			fractionBox.addChild(fractionSlashBox);
			fractionBox.addChild(denominatorBox);
			
			// fill boxes
			
			if (numerator.length == 0){
				addCharacter(" ", numeratorBox);
			} else {
				for each (var numeratorChar:String in numerator.split("")){
					addCharacter(numeratorChar, numeratorBox);
				}
			}
			if (denominator.length == 0){
				addCharacter(" ", denominatorBox);
			} else {
				for each (var denominatorChar:String in denominator.split("")){
					addCharacter(denominatorChar, denominatorBox);
				}
			}
			
			// fraction aligns
			
			var tmpLabel:Label = new Label;
			tmpLabel.regenerateStyleCache(false);
			if (tmpLabel.measureText(numerator).width > tmpLabel.measureText(denominator).width){
				denominatorBox.percentWidth = 100;
				denominatorBox.setStyle("horizontalAlign", "center");
			} else {
				numeratorBox.percentWidth = 100;
				numeratorBox.setStyle("horizontalAlign", "center");
			} 
			
			// add child
			
			fractionBox.setStyle("letterSpacing", 0);
			container.addChild(fractionBox);
		}
		
		private function clearText():void{
			removeAllChildren();
		}

		private function resizeHandler(re:ResizeEvent):void{
			updateDecimalDisplay();
		}
		
		private function updateDecimalDisplay():void{
			if (autoSize  &&  alignDecimal  &&  text.length > 1){
				if (text.charAt(0) == ","  ||  text.charAt(0) == "."  ||  
					text.charAt(text.length-1) == ","  ||  text.charAt(text.length-1) == "."){
					
					for each (var dObj:DisplayObject in body.getChildren()){
						if (dObj is Spacer){
							body.removeChild(dObj);
						}
					}
					
					var mcc:MathTextCharacter = new MathTextCharacter;
					var fontSize:Number = getStyle("fontSize");
					mcc.setStyle("fontSize", fontSize);
					mcc.regenerateStyleCache(false);
					var decimalWidth:Number;					
					var contentWidth:Number;
					var spaceWidth:Number;
					var contentString:String;
					
					if (text.charAt(0) == ","){
						decimalWidth = mcc.measureText(",").width;
					} else if (text.charAt(0) == "."){
						decimalWidth = mcc.measureText(".").width;
					} else if (text.charAt(text.length-1) == ","){
						decimalWidth = mcc.measureText(",").width;
					} else if (text.charAt(text.length-1) == "."){
						decimalWidth = mcc.measureText(".").width;
					} else{
						decimalWidth = mcc.measureText(",").width;
					}
					
					if (text.charAt(0) == ","  ||  text.charAt(0) == "."){
						contentString = text.substr(1);
					} else {
						contentString = text.substr(0, text.length-1);	
					}
					
					contentWidth = mcc.measureText(contentString).width;
					
					spaceWidth = Math.ceil( (body.width - contentWidth ) / 2 - decimalWidth - 2 );
					
					if (spaceWidth < 0)
						spaceWidth = 0;
					
					var sp1:Spacer = new Spacer;
					sp1.width = spaceWidth;
					
					if (text.charAt(0) == ","  ||  text.charAt(0) == "."){
						body.addChildAt(sp1, 1);
						body.setStyle("horizontalAlign", "left");
					} else {
						body.addChildAt(sp1, body.getChildren().length-1);
						body.setStyle("horizontalAlign", "right");
					}
				} else {
					body.setStyle("horizontalAlign", "center");
				}
			} else {
				body.setStyle("horizontalAlign", "center");
			}
		}
		
		// -------------------------- MEASURE ------------------------------
		
		override public function measureText(t:String):TextLineMetrics{
			
			text = t;
			var obj:*;
			var char:MathTextCharacter;
			var tmp_tlm:TextLineMetrics;
			var main_tlm:TextLineMetrics = new TextLineMetrics(0, 0, 0, 0, 0, 0);
			
			body.regenerateStyleCache(false);
			
			for each (obj in body.getChildren()){
				if (obj is MathTextCharacter){
					char = obj as MathTextCharacter;
					char.regenerateStyleCache(false);
					tmp_tlm = char.measureText(char.text);
					main_tlm.width += tmp_tlm.width;
					if (tmp_tlm.height > main_tlm.height)
						main_tlm.height = tmp_tlm.height;
				} else if (obj is VBox){
					
					obj.regenerateStyleCache(false);
					
					var num_tlm:TextLineMetrics = new TextLineMetrics(0,0,0,0,0,0);
					var den_tlm:TextLineMetrics = new TextLineMetrics(0,0,0,0,0,0);
					
					for each (var numChar:MathTextCharacter in obj.getChildren()[0].getChildren()){
						numChar.regenerateStyleCache(false);
						tmp_tlm = numChar.measureText(numChar.text);
						num_tlm.width += tmp_tlm.width;
						if (tmp_tlm.height > num_tlm.height)
							num_tlm.height = tmp_tlm.height;
					}
					
					for each (var denChar:MathTextCharacter in obj.getChildren()[2].getChildren()){
						denChar.regenerateStyleCache(false);
						tmp_tlm = denChar.measureText(denChar.text);
						den_tlm.width += tmp_tlm.width;
						if (tmp_tlm.height > den_tlm.height)
							den_tlm.height = tmp_tlm.height;
					}
					
					main_tlm.width += (num_tlm.width > den_tlm.width) ? num_tlm.width : den_tlm.width ;
					
					var h:Number = num_tlm.height + den_tlm.height + 2*obj.getStyle("verticalGap") + obj.getChildren()[1].height;
					
					if (h > main_tlm.height){
						main_tlm.height = h;
					}
					
				}
			}
			
			return main_tlm;
		}
		
		// -------------------------- STYLE ------------------------------
		
		override public function styleChanged(styleProp:String):void{
			super.styleChanged(styleProp);
			
			for each (var slashBox:Box in slashes){
				slashBox.setStyle("backgroundColor", getStyle("color"));
				slashBox.height = Math.ceil(getStyle("fontSize")/16);
			}
		}
		

		// -------------------------- STATIC ------------------------------
	}
}